/**
 * 
 */
package iit.projects.etaxi.model.dao;

import iit.projects.etaxi.model.entity.Driver;
import iit.projects.etaxi.model.entity.SubCity;
import iit.projects.etaxi.model.entity.Vehicle;
import iit.projects.etaxi.model.entity.VehicleLocation;
import iit.projects.etaxi.util.Constants;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * @author naveen
 *
 */
public class HibernateVehicleDao extends HibernateDaoSupport implements VehicleDao {
	
	final Logger log = LoggerFactory.getLogger(HibernateReservationDao.class);

	@SuppressWarnings("unchecked")
	@Override
	public List<Vehicle> findVehicles() {
		List<Vehicle> list = getHibernateTemplate().find("from Vehicle");
		return list;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Vehicle> findAvailableVehiclesInProximity(SubCity subCity, Date startTime, Date endTime){
		if(log.isDebugEnabled()){
			log.debug("Going to find Vehicles close to " + subCity.getName() + " between " + startTime + " and " + endTime);
		}
		
		Session session = getSession(false);
		List<SubCity> subCities = new ArrayList<SubCity>();
		subCities.add(subCity);
		subCities.addAll(subCity.getSubCitiesInProximity());
		
		try{
			Criteria criteria = session.createCriteria(VehicleLocation.class);
			criteria.add(Restrictions.eq("status", Constants.VehicleLocationStatus.LAST_ASSIGNED));
			criteria.add(Restrictions.between("time", startTime, endTime));
			criteria.add(Restrictions.in("location", subCities));
			criteria = criteria.createCriteria("vehicle");
			criteria = criteria.createCriteria("driver");
			criteria.add(Restrictions.eq("available", true));
			
			List<VehicleLocation> list = criteria.list();
			
			if(list != null && !list.isEmpty()){
				List<Vehicle> vehicleList = new ArrayList<Vehicle>();
				for(VehicleLocation location : list){
					vehicleList.add(location.getVehicle());
				}
				return vehicleList;
			}
			
			return null;
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Vehicle> findAvailableVehicles(){
		if(log.isDebugEnabled()){
			log.debug("Going to find Available Vehicles");
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(Vehicle.class);
			criteria.add(Restrictions.eq("onHire", false));
			criteria = criteria.createCriteria("driver");
			criteria.add(Restrictions.eq("available", true));
			//criteria.add(Restrictions.eq("driver.available", Boolean.TRUE));
			
			List<Vehicle> vehicles = criteria.list();
			return vehicles;
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public Vehicle updateVehicle(Vehicle vehicle){
		if(log.isDebugEnabled()){
			log.debug("Going to update Vehicle " + vehicle.getId());
		}
		
		Session session = getSession(false);
		try{
			return (Vehicle)session.merge(vehicle);
		}catch(HibernateException ex){
			log.error("ERROR : " + ex);
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public List<Vehicle> updateVehicles(List<Vehicle> vehicles){
		if(vehicles == null || vehicles.isEmpty()){
			return null;
		}
		
		Session session = getSession(false);
		try{
			List<Vehicle> updatedVehicles = new ArrayList<Vehicle>();
			for(Vehicle vehicle : vehicles){
				updatedVehicles.add((Vehicle)session.merge(vehicle));
			}
			
			return updatedVehicles;
		}catch(HibernateException ex){
			log.error("ERROR : " + ex);
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public void deleteVehicleLocations(List<VehicleLocation> vehicleLocations){
		if(vehicleLocations == null || vehicleLocations.isEmpty()){
			return;
		}
		
		Session session = getSession(false);
		try{
			for(VehicleLocation vehicleLocation : vehicleLocations){
				session.delete(vehicleLocation);
			}
		}catch(HibernateException ex){
			log.error("ERROR : " + ex);
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public void saveDriver(Driver driver){
		if(log.isDebugEnabled()){
			log.debug("Going to find Save Driver");
		}
		
		Session session = getSession(false);
		
		try{
			session.persist(driver);
		}catch(HibernateException ex){
			log.error("ERROR : " + ex);
			throw convertHibernateAccessException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<VehicleLocation> findVehicleLocationsByStatusByReservationStatusBefore(String locationStatus, 
																					   String reservationStatus,
																					   Date time){
		if(log.isDebugEnabled()){
			log.debug("Going to find VecicleLocations of status " + locationStatus + " before " + time);
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(VehicleLocation.class);
			criteria.add(Restrictions.eq("status", locationStatus));
			criteria.add(Restrictions.lt("time", time));
			criteria = criteria.createCriteria("reservation");
			criteria.add(Restrictions.eq("status", reservationStatus));
			
			List<VehicleLocation> vehicleLocations = criteria.list();
			return vehicleLocations;
			
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}

}
